package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.mapper.ApArticleConfigMapper;
import com.heima.article.mapper.ApArticleContentMapper;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.ApArticleConfigService;
import com.heima.article.service.ApArticleService;
import com.heima.article.service.ArticleFreemarkerService;
import com.heima.common.constants.ArticleConstants;
import com.heima.common.constants.HotArticleConstants;
import com.heima.common.redis.CacheService;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.pojos.ApArticleConfig;
import com.heima.model.article.pojos.ApArticleContent;
import com.heima.model.article.vos.HotArticleVo;
import com.heima.model.behavior.dtos.ArticleInfoDto;
import com.heima.model.behavior.dtos.CollectionBehaviorDto;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.mess.ArticleVisitStreamMess;
import com.heima.model.mess.UpdateArticleMess;
import com.heima.model.wemedia.dtos.WmFindNewsCommentDto;
import com.heima.model.wemedia.dtos.WmNewsPageDto;
import com.heima.model.wemedia.dtos.WmUpdateCommentStatusDto;
import com.heima.model.wemedia.vos.WmCommentVo;
import com.heima.model.wemedia.vos.WmNewsPageVo;
import com.heima.utils.thread.AppThreadLocalUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestBody;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {

    @Autowired
    private ApArticleMapper apArticleMapper;

    @Autowired
    private ApArticleConfigMapper apArticleConfigMapper;

    @Autowired
    private ApArticleContentMapper apArticleContentMapper;

    @Autowired
    private ArticleFreemarkerService articleFreemarkerService;

    @Autowired
    private CacheService cacheService;

    @Autowired
    private KafkaTemplate kafkaTemplate;

    @Autowired
    private ApArticleConfigService apArticleConfigService;

    // 分页最大显示条数
    private final static short MAX_PAGE_SIZE = 50;

    @Override
    public ResponseResult load(Short loadtype, ArticleHomeDto articleHomeDto) {
        // 校验分页参数
        Integer size = articleHomeDto.getSize();
        if (size==null || size==0){
            size=10;
        }
        //设置最大显示50条 返回最小数
        size=Math.min(size,MAX_PAGE_SIZE);
        articleHomeDto.setSize(size);

        //校验频道
        String tag = articleHomeDto.getTag();
        if (StringUtils.isBlank(tag)){
            tag=ArticleConstants.DEFAULT_TAG;
        }

        // 校验加载类型
        if (!loadtype.equals(ArticleConstants.LOADTYPE_LOAD_MORE) && !loadtype.equals(ArticleConstants.LOADTYPE_LOAD_NEW)){
            loadtype=ArticleConstants.LOADTYPE_LOAD_MORE;
        }

        //校验时间
        if (articleHomeDto.getMinBehotTime()==null){articleHomeDto.setMinBehotTime(new Date());}
        if (articleHomeDto.getMaxBehotTime()==null){articleHomeDto.setMaxBehotTime(new Date());}

        List<ApArticle> apArticles = apArticleMapper.loadApArticle(articleHomeDto, loadtype);

        return ResponseResult.okResult(apArticles);
    }

    /**
     * 同步自媒体端文章
     * @param articleDto
     * @return
     */
    @Override
    public ResponseResult saveArticle(ArticleDto articleDto) {
        //检查参数
        if (articleDto==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        ApArticle article=new ApArticle();
        //属性拷贝
        BeanUtils.copyProperties(articleDto,article);
        //判断是否存在id
        if (articleDto.getId()==null){
            //保存文章
            save(article);
            //保存配置
            ApArticleConfig apArticleConfig=new ApArticleConfig(article.getId());
            apArticleConfigMapper.insert(apArticleConfig);

            //保存文章内容
            ApArticleContent apArticleContent=new ApArticleContent();
            apArticleContent.setArticleId(article.getId());
            apArticleContent.setContent(articleDto.getContent());
            apArticleContentMapper.insert(apArticleContent);
        }else {
            //存在id 修改
            updateById(article);
            //修改文章内容
            ApArticleContent apArticleContent=apArticleContentMapper.selectOne(Wrappers.
                    <ApArticleContent>lambdaQuery().eq(ApArticleContent::getArticleId,articleDto.getId()));
            apArticleContent.setContent(articleDto.getContent());
            apArticleContentMapper.updateById(apArticleContent);
        }

        //异步调用 生成静态文件上传到minio中
        articleFreemarkerService.buildArticleToMinIO(article,articleDto.getContent());

        //返回id
        return ResponseResult.okResult(article.getId());
    }

    /**
     * 收藏
     * @param collectionBehaviorDto
     * @return
     */
    @Override
    public ResponseResult collectionBehavior(CollectionBehaviorDto collectionBehaviorDto) {
        //参数校验
        if (collectionBehaviorDto==null || collectionBehaviorDto.getEntryId()==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //文章id
        Long entryId = collectionBehaviorDto.getEntryId();
        //用户id
        Integer apUserId = AppThreadLocalUtils.getUser().getId();

        //实时更新 存入数据库
        UpdateArticleMess mess = new UpdateArticleMess();
        mess.setArticleId(entryId);
        mess.setType(UpdateArticleMess.UpdateArticleType.COLLECTION);

        if (collectionBehaviorDto.getOperation()==0){
            cacheService.hPut(ArticleConstants.USER_COLLECTION_BEHAVIOR,apUserId+"::"+entryId,"0");

            mess.setAdd(1);

        }else {
            cacheService.hDelete(ArticleConstants.USER_COLLECTION_BEHAVIOR,apUserId+"::"+entryId);

            mess.setAdd(-1);
        }
        //同步数据库
        //kafkaTemplate.send(ArticleConstants.COLLECTION_BEHAVIOR, JSON.toJSONString(collectionBehaviorDto));

        //发送消息，数据聚合
        kafkaTemplate.send(HotArticleConstants.HOT_ARTICLE_SCORE_TOPIC,JSON.toJSONString(mess));


        return ResponseResult.okResult("");
    }

    /**
     * 行为数据回显
     * @param articleInfoDto
     * @return
     */
    @Override
    public ResponseResult loadArticleBehavior(ArticleInfoDto articleInfoDto) {
        //数据校验
        if (articleInfoDto==null || articleInfoDto.getAuthorId()==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //用户id
        Integer apUserId = AppThreadLocalUtils.getUser().getId();
        //文章id
        Long articleId = articleInfoDto.getArticleId();
        //作者id
        Integer authorId = articleInfoDto.getAuthorId();

        Map<String,Object> map=new HashMap<>();
        boolean likeResult =cacheService.hExists(ArticleConstants.USER_LIKES_BEHAVIOR,
                apUserId + "::" + articleId);
        if (likeResult){
            map.put("islike",true);
        }else {
            map.put("islike",false);
        }

        boolean unLikeResult =cacheService.hExists(ArticleConstants.USER_UNLIKES_BEHAVIOR,
                apUserId + "::" + articleId);
        if (unLikeResult){
            map.put("isunlike",true);
        }else {
            map.put("isunlike",false);
        }

        boolean isCollectionResult =cacheService.hExists(ArticleConstants.USER_COLLECTION_BEHAVIOR,
                apUserId + "::" + articleId);
        if (isCollectionResult){
            map.put("iscollection",true);
        }else {
            map.put("iscollection",false);
        }

        boolean isFollowResult =cacheService.hExists(ArticleConstants.USER_RELATION_FOLLOW, apUserId + "::" + authorId);
        if (isFollowResult){
            map.put("isfollow",true);
        }else {
            map.put("isfollow",false);
        }
        return ResponseResult.okResult(map);
    }


    /**
     * 加载文章列表
     * @param dto
     * @param type      1 加载更多   2 加载最新
     * @param firstPage true  是首页  flase 非首页
     * @return
     */
    @Override
    public ResponseResult load2(ArticleHomeDto dto, Short type, boolean firstPage) {
        if(firstPage){
            String jsonStr = cacheService.get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + dto.getTag());
            if(StringUtils.isNotBlank(jsonStr)){
                List<HotArticleVo> hotArticleVoList = JSON.parseArray(jsonStr, HotArticleVo.class);
                ResponseResult responseResult = ResponseResult.okResult(hotArticleVoList);
                return responseResult;
            }
        }
        return load(type,dto);
    }


    /**
     * 更新文章的分值  同时更新缓存中的热点文章数据
     * @param mess
     */
    @Override
    public void updateScore(ArticleVisitStreamMess mess) {

        // 获取老分数
        Integer oldScore=getOldScore(mess);

        //1.更新文章的阅读、点赞、收藏、评论的数量
        ApArticle apArticle = updateArticle(mess);
        //2.计算文章的分值
        Integer newScore = computeScore(mess);

        //总分数
        Integer score=oldScore+newScore*3;

        //3.替换当前文章对应频道的热点数据
        replaceDataToRedis(apArticle, score, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + apArticle.getChannelId());

        //4.替换推荐对应的热点数据
        replaceDataToRedis(apArticle, score, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + ArticleConstants.DEFAULT_TAG);

    }


    /**
     * 获取文章老分数
     * @param mess
     * @return
     */
    private Integer getOldScore(ArticleVisitStreamMess mess) {
        Integer oldScore=0;
        //获取文章
        ApArticle apArticle = getById(mess.getArticleId());
        if (apArticle.getViews()!=null){
            oldScore+=apArticle.getViews();
        }
        if (apArticle.getLikes()!=null){
            oldScore=oldScore+apArticle.getLikes() * ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }
        if (apArticle.getCollection()!=null){
            oldScore=oldScore+apArticle.getCollection() * ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }
        if (apArticle.getComment()!=null){
            oldScore=oldScore+apArticle.getComment() * ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        return oldScore;
    }

    /**
     * 替换数据并且存入到redis
     * @param apArticle
     * @param score
     * @param s
     */
    private void replaceDataToRedis(ApArticle apArticle, Integer score, String s) {
        String articleListStr = cacheService.get(s);
        if (StringUtils.isNotBlank(articleListStr)) {
            List<HotArticleVo> hotArticleVoList = JSON.parseArray(articleListStr, HotArticleVo.class);

            boolean flag = true;

            //如果缓存中存在该文章，只更新分值
            for (HotArticleVo hotArticleVo : hotArticleVoList) {
                if (hotArticleVo.getId().equals(apArticle.getId())) {
                    hotArticleVo.setScore(score);
                    flag = false;
                    break;
                }
            }

            //如果缓存中不存在，查询缓存中分值最小的一条数据，进行分值的比较，如果当前文章的分值大于缓存中的数据，就替换
            if (flag) {
                if (hotArticleVoList.size() >= 30) {
                    hotArticleVoList = hotArticleVoList.stream().sorted(Comparator.comparing(HotArticleVo::getScore)
                            .reversed()).collect(Collectors.toList());
                    HotArticleVo lastHot = hotArticleVoList.get(hotArticleVoList.size() - 1);
                    if (lastHot.getScore() < score) {
                        hotArticleVoList.remove(lastHot);
                        HotArticleVo hot = new HotArticleVo();
                        BeanUtils.copyProperties(apArticle, hot);
                        hot.setScore(score);
                        hotArticleVoList.add(hot);
                    }


                } else {
                    HotArticleVo hot = new HotArticleVo();
                    BeanUtils.copyProperties(apArticle, hot);
                    hot.setScore(score);
                    hotArticleVoList.add(hot);
                }
            }
            //缓存到redis
            hotArticleVoList = hotArticleVoList.stream().sorted(Comparator.comparing(HotArticleVo::getScore)
                    .reversed()).collect(Collectors.toList());
            cacheService.set(s, JSON.toJSONString(hotArticleVoList));

        }
    }

    /**
     * 更新文章行为数量
     * @param mess
     */
    private ApArticle updateArticle(ArticleVisitStreamMess mess) {
        ApArticle apArticle = getById(mess.getArticleId());
        apArticle.setCollection((apArticle.getCollection()==null?0:apArticle.getCollection())+mess.getCollect());
        apArticle.setComment((apArticle.getComment()==null?0:apArticle.getComment())+mess.getComment());
        apArticle.setLikes((apArticle.getLikes()==null?0:apArticle.getLikes())+mess.getLike());
        apArticle.setViews((apArticle.getViews()==null?0:apArticle.getViews())+mess.getView());
        updateById(apArticle);
        return apArticle;

    }

    /**
     * 计算文章当天具体分值
     * @param mess
     * @return
     */
    private Integer computeScore(ArticleVisitStreamMess mess) {
        Integer score = 0;
        score += mess.getLike() * ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        score += mess.getView();
        score += mess.getComment() * ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        score += mess.getCollect() * ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        return score;
    }


    /**
     * 远程获取文章配置
     * @param id
     * @return
     */
    @Override
    public ApArticleConfig getArticleConfig(Long id) {
        ApArticleConfig apArticleConfig = apArticleConfigService.getOne(Wrappers.
                <ApArticleConfig>lambdaQuery().eq(ApArticleConfig::getArticleId, id));
        return apArticleConfig;
    }

    /**
     * 远程获取文章评论管理列表
     * @param id
     * @return
     */
    @Override
    public PageResponseResult getCommentManageList(WmFindNewsCommentDto dto,Integer id) {
        //分页构建器
        Page<ApArticle> apArticlePage=new Page<>(dto.getPage(),dto.getSize());
        //条件构建器
        LambdaQueryWrapper<ApArticle> lqw =new LambdaQueryWrapper<>();
        lqw.eq(ApArticle::getAuthorId,id);
        Date beginDate = null;
        Date endDate = null;
        try {
            //字符串时间转日期
            DateFormat fmt=new SimpleDateFormat("yyyy-MM-dd");
            beginDate = fmt.parse(dto.getBeginDate());
            endDate = fmt.parse(dto.getEndDate());
        } catch (ParseException e) {
            e.printStackTrace();
        }
        //时间条件
        if (dto.getBeginDate()!=null && dto.getEndDate()!=null){
            lqw.ge(ApArticle::getCreatedTime,beginDate);
            lqw.le(ApArticle::getCreatedTime,endDate);
        }
        //排序
        lqw.orderByDesc(ApArticle::getCreatedTime);

        List<ApArticle> apArticleList = page(apArticlePage, lqw).getRecords();

        List<WmCommentVo> wmCommentVoList=new ArrayList<>();
        //遍历
        for (ApArticle article : apArticleList) {
            WmCommentVo wmCommentVo=new WmCommentVo();
            wmCommentVo.setComments(article.getComment());
            wmCommentVo.setTitle(article.getTitle());
            wmCommentVo.setId(article.getId().toString());
            ApArticleConfig apArticleConfig = apArticleConfigService.getOne(Wrappers.<ApArticleConfig>lambdaQuery().
                    eq(ApArticleConfig::getArticleId, article.getId()));
            wmCommentVo.setIsComment(apArticleConfig.getIsComment());

            wmCommentVoList.add(wmCommentVo);
        }
        PageResponseResult pageResponseResult=new PageResponseResult(dto.getPage(),dto.getSize(), Math.toIntExact(apArticlePage.getTotal()));
        pageResponseResult.setData(wmCommentVoList);

        return pageResponseResult;
    }

    /**
     * 远程修改文章评论状态
     * @param dto
     * @return
     */
    @Override
    public ResponseResult updateCommentStatus(WmUpdateCommentStatusDto dto) {
        //参数校验
        if (dto==null && dto.getArticleId()==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        ApArticle apArticle = getById(dto.getArticleId());
        //获取文章配置
        ApArticleConfig apArticleConfig = apArticleConfigService.getOne(Wrappers.<ApArticleConfig>lambdaQuery().
                eq(ApArticleConfig::getArticleId, dto.getArticleId()));
        if (dto.getOperation()==0){
            //关闭评论
            apArticleConfig.setIsComment(false);
            //文章评论数恢复为0
            apArticle.setComment(0);
            //关闭评论 删除评论
            kafkaTemplate.send(ArticleConstants.ARTICLE_COMMENT_STATUS,dto.getArticleId());

        }else {
            apArticleConfig.setIsComment(true);
        }
        apArticleConfigService.updateById(apArticleConfig);
        //修改文章
        updateById(apArticle);
        return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
    }

    /**
     * 获取作者文章
     * @param beginDate
     * @param endDate
     * @param userId
     * @return
     */
    @Override
    public List<ApArticle> getAllArticle(Date beginDate, Date endDate,Integer userId) {
        //条件构造
        LambdaQueryWrapper<ApArticle> lqw=new LambdaQueryWrapper<>();
        lqw.eq(ApArticle::getAuthorId, userId);
        lqw.between(ApArticle::getCreatedTime,beginDate,endDate);

        List<ApArticle> apArticleList = list(lqw);
        return apArticleList;
    }


    /**
     * 获取作者文章 分页展示
     * @param dto
     * @param wmUserId
     * @return
     */
    @Override
    public ResponseResult getNewsPage(WmNewsPageDto dto, Integer wmUserId) {
        //条件构造
        LambdaQueryWrapper<ApArticle> lqw=new LambdaQueryWrapper<>();
        lqw.eq(ApArticle::getAuthorId,wmUserId);

        Date beginDate = null;
        Date endDate = null;
        try {
            SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
            beginDate = sdf.parse(dto.getBeginDate());
            endDate = sdf.parse(dto.getEndDate());
        } catch (ParseException e) {
            e.printStackTrace();
        }

        //处理为当天的日期
        endDate=new Date(endDate.getTime()+1000*60*60*24-1);
        lqw.between(ApArticle::getCreatedTime,beginDate,endDate);

        Page<ApArticle> apArticlePage =new Page<>(dto.getPage(),dto.getSize());
        //获取文章
        List<ApArticle> apArticleList = page(apArticlePage, lqw).getRecords();

        List<WmNewsPageVo> wmNewsPageVoList=new ArrayList<>();
        for (ApArticle article : apArticleList) {
            WmNewsPageVo wmNewsPageVo=new WmNewsPageVo();
            wmNewsPageVo.setId(article.getId());
            wmNewsPageVo.setTitle(article.getTitle());
            wmNewsPageVo.setLikes(article.getLikes());
            wmNewsPageVo.setCollection(article.getCollection());
            wmNewsPageVo.setViews(article.getViews());

            wmNewsPageVoList.add(wmNewsPageVo);
        }

        PageResponseResult pageResponseResult=new PageResponseResult(dto.getPage(),dto.getSize(),apArticleList.size());
        pageResponseResult.setData(wmNewsPageVoList);

        return pageResponseResult;
    }

}
